﻿using Padsurface.Audio.Engine.Interfaces;
using Padsurface.Audio.Song.Repositories.Interfaces;
using Padsurface.Audio.Song.Services.Interfaces;
using System;
using System.Linq;
using System.Composition;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using System.Collections.Generic;
using Padsurface.Audio.Song.Model;

namespace Padsurface.Audio.Song.Services
{
    [Export(typeof(ISongLoader))]
    internal class SongLoader : ISongLoader
    {
        private readonly ISongRepository _songRepository;
        private readonly IMetronome _metronome;
        private readonly IPlayBuffer _playBuffer;

        [ImportingConstructor]
        public SongLoader(ISongRepository songRepository, IMetronome metronome, IPlayBuffer playBuffer)
        {
            if (songRepository == null) throw new ArgumentNullException("songRepository");
            if (metronome == null) throw new ArgumentNullException("metronome");
            if (playBuffer == null) throw new ArgumentNullException("playBuffer");

            _songRepository = songRepository;
            _metronome = metronome;
            _playBuffer = playBuffer;
        }

        public async Task<Model.Song> Load(StorageFile storageFile)
        {
            if (storageFile == null) throw new ArgumentNullException("storageFile");

            var deserializedSong = await _songRepository.Get(storageFile);

            var song = await CreateSong(deserializedSong);

            return song;
        }

        private async Task<Model.Song> CreateSong(Model.SerializableSong deserializedSong)
        {
            var song = new Model.Song(_metronome, _playBuffer, deserializedSong.Id)
            {
                Name = deserializedSong.Name,
                IsLoopEnabled = deserializedSong.IsLoopEnabled,
                BeatsPerMinute = deserializedSong.BeatsPerMinute
            };

            var sounds = await LoadUniqueSounds(deserializedSong);
            song.Sounds.AddRange(sounds);

            foreach (var recordedSound in deserializedSong.Timeline)
            {
                song.Timeline.Add(
                    new RecordedSound(recordedSound.Beat, sounds.Single(s => s.WaveFileOutput.FilePath == recordedSound.SoundFilePath)));
            }

            return song;
        }

        private async Task<IEnumerable<Model.Sound>> LoadUniqueSounds(Model.SerializableSong deserializedSong)
        {
            var sounds = new List<Model.Sound>();

            foreach (var soundFilePath in deserializedSong.SoundFilePaths.Distinct())
            {
                try
                {
                    var soundFile = await StorageFile.GetFileFromPathAsync(soundFilePath);
                    sounds.Add(new Model.Sound(new Wave.WaveFileOutput(soundFile)));
                }
                catch (FileNotFoundException ex)
                {
                    Debug.WriteLine(ex);
                }
            }

            return sounds;
        }
    }
}